/*=============================================================================
#    The element-comm is a tentative program used to test our ideas.           
#                       Copyright (C)  bgao                                    
#
#     This program is free software; you can redistribute it and/or            
#      modify it under the terms of the GNU General Public License             
#     as published by the Free Software Foundation; either version 2           
#         of the License, or (at your option) any later version.               
#
#     This program is distributed in the hope that it will be useful,          
#    but WITHOUT ANY WARRANTY; without even the implied warranty of           
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            
#             GNU General Public License for more details.                    
#
#    You should have received a copy of the GNU General Public License        
#     along with this program; if not, write to the Free Software             
#      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,             
#                       MA  02110-1301, USA.                                  
#==============================================================================
# $Id: mac_assoc.c 348 2010-11-29 09:01:16Z bgao $ 
#
# $Author: bgao $ 
#
# $Revision: 348 $ 
#
# $Date: 2010-11-29 17:01:16 +0800 (星期一, 29 十一月 2010) $ 
#
# Description: 
#
=============================================================================*/
/*! @file mac_assoc.c
 *  @brief This file implements the mac association mechanism.
 */
#include <stdio.h>
#include "mac.h"

/*! the definition of MAC 64-bits extended address. */
extern uint64 aExtendedAddress;

/*! the mac association request primitive, which should be used to start
 *  the association process.
 *  @param req pointer to the structure of the association request.
 *  @return returns MAC_OK(0) if everything goes well else return MAC_ERR(-1).
 */
int8 mac_assoc_req(mac_assoc_req_t *req)
{
    buf_t *buf;
    addr_t *coord = &req->coord;
    mac_frm_assoc_req_t req_frm;
    uint8 ret;
    mac_assoc_cfm_t cfm;
    bool en_ack = 0;
    bool en_indir = 0;

    /* set state of mac */
    mac_set_state(mac_assoc_reqing);
    /* set phy pib */
    rf_set_chnl(req->logic_chnl);
    /* rf_set_chnls_supported(); */
    /* rf_set_cca_mode(); */
    /* rf_set_current_page(req->chnl_page); */

    /* set mac pib */
    mac_set_pan_id(req->coord_pan_id);
    if (coord->mode == ADDR_64_M) {
        mac_set_coord_extended_addr(coord->a.addr64);
        req_frm.dest.a.addr64 = coord->a.addr64;
    } else {
        mac_set_coord_short_addr(coord->a.addr16);
        req_frm.dest.a.addr16 = coord->a.addr16;
    }

    /* dest and src addressing mode shall each be set to 64-bit extended addressing */
    req_frm.dest.mode = coord->mode;
    req_frm.src.mode = ADDR_64_M;
    req_frm.src.a.addr64 = mac_get_extended_addr();/*aExtendedAddress*/
    req_frm.src_pan_id = BROADCAST_PAN_ID;
    req_frm.dest_pan_id = req->coord_pan_id;
    req_frm.cap_info = req->cap_info;
    req_frm.dsn = mac_get_dsn();

    buf = buf_alloc(true);
    if (buf == NULL) {
        ret = MAC_TRANSACTION_OVERFLOW;
        MAC_DEBUG("buf_alloc() returns a NULL.\n");
        goto req_frm_err_process;
    }

    ret = mac_gen_assoc_req(buf, &req_frm);
    if (ret != MAC_OK) {
        goto req_frm_err_process;
    }

    MAC_DEBUG("mac_req_frm: got here\n");
    MAC_DEBUG("mac_req_frm: buf->length is %d\n", buf->length);
    /* need ack, direct transmit */
    en_ack = 1;
    en_indir = 0;
    ret = mac_handle_tx(buf, coord, en_ack, en_indir, req->handle, req_frm.dsn);
    MAC_DEBUG("mac_req_frm: return value of mac_handle_tx is %d\n", ret);

    /* err process */
    if (ret != MAC_OK) {
        goto req_frm_err_process;
    }
    MAC_DEBUG("mac_req_frm: got here\n");

    /* set state of mac */
    mac_set_state(mac_assoc_reqing);

    /* TODO free the buffer when receive a akc or no ack received */
    return MAC_OK;

req_frm_err_process:
    cfm.addr.mode = coord->mode;
    /* not sure the mode of the address, so copy the whole union */
    cfm.addr.a.addr64 = coord->a.addr64;
    cfm.status = ret;
    mac_assoc_cfm(&cfm);

    buf_free(buf);

    return MAC_ERR;
}

/*! mac association response primitive, which is used to give the result
 *  of the association process by the coordinator.
 *  @param resp pointer to the structure of mac assocition response primitive.
 *  @return the status of this primitive. 
 *          returns MAC_OK(0) if everything goes well else return MAC_ERR(-1).
 */
int8 mac_assoc_resp(mac_assoc_resp_t *resp)
{
    int8 ret;
    mac_frm_assoc_resp_t resp_frm;
    buf_t *buf;
    addr_t dest;
    mac_comm_status_indict_t comm;

    resp_frm.src.mode = ADDR_64_M;
    resp_frm.src.a.addr64 = mac_get_extended_addr();
    resp_frm.dest.mode = ADDR_64_M;
    resp_frm.dest.a.addr64 = resp->dest_addr;
    resp_frm.status = resp->status;
    resp_frm.dsn = mac_get_dsn();
    resp_frm.pan_id = mac_get_pan_id();
    resp_frm.short_addr = resp->short_addr;

    buf = buf_alloc(true);
    if (buf == NULL) {
        ret = MAC_TRANSACTION_OVERFLOW;
        goto assoc_resp_err_process;
    }

    ret = mac_gen_assoc_resp(buf, &resp_frm);
    if (ret != MAC_OK) {
        goto assoc_resp_err_process;
    }

    dest.mode = ADDR_64_M;
    dest.a.addr64 = resp->dest_addr;
    ret = mac_handle_tx(buf, &dest, 1, 1, resp->handle, resp_frm.dsn);
    if (ret != MAC_OK) {
        goto assoc_resp_err_process;
    }

    /* TODO set a timer to time out macTransactionPersistenceTime symbols later. */
    return MAC_OK;

    /*
    typedef struct mac_comm_status_indict_t_ {
        uint16 pan_id;
        addr_t src_addr;
        addr_t dest_addr;
        mac_status_t status;
    } mac_comm_status_indict_t; 
    */
assoc_resp_err_process:
    comm.pan_id = resp_frm.pan_id;
    comm.src_addr.a.addr16 = resp_frm.src.a.addr16;
    comm.dest_addr.a.addr64 = resp_frm.dest.a.addr64;
    comm.status = ret;

    mac_comm_status_indict(&comm);

    buf_free(buf);

    return ret;
}

